"""
健康检查模块 - 提供应用和依赖服务的健康状态检查
支持数据库、Redis、HTTP服务、系统资源等健康检查
"""

import asyncio
import time
import psutil
import aiohttp
import asyncpg
import aioredis
from typing import (
    Dict, List, Optional, Any, Callable, Union, Protocol,
    runtime_checkable, Awaitable
)
from dataclasses import dataclass, field
from enum import Enum
from abc import ABC, abstractmethod
from datetime import datetime, timedelta

from ..logging.manager import get_logger
from ..events.bus import get_event_bus, Event

class HealthStatus(Enum):
    """健康状态"""
    HEALTHY = "healthy"        # 健康
    UNHEALTHY = "unhealthy"    # 不健康
    DEGRADED = "degraded"      # 降级
    UNKNOWN = "unknown"        # 未知

@dataclass
class HealthCheck:
    """健康检查结果"""
    name: str
    status: HealthStatus
    message: Optional[str] = None
    details: Dict[str, Any] = field(default_factory=dict)
    duration: Optional[float] = None
    timestamp: Optional[datetime] = None
    error: Optional[str] = None

@runtime_checkable
class IHealthChecker(Protocol):
    """健康检查器接口"""
    
    async def check(self) -> HealthCheck:
        """执行健康检查"""
        ...
    
    @property
    def name(self) -> str:
        """检查器名称"""
        ...

class BaseHealthChecker(ABC):
    """健康检查器基类"""
    
    def __init__(self, name: str, timeout: float = 5.0):
        self.checker_name = name
        self.timeout = timeout
        self.logger = get_logger(f"health_check.{name}")
    
    @property
    def name(self) -> str:
        return self.checker_name
    
    @abstractmethod
    async def _check(self) -> HealthCheck:
        """具体的健康检查逻辑"""
        pass
    
    async def check(self) -> HealthCheck:
        """执行健康检查"""
        start_time = time.time()
        
        try:
            # 设置超时
            result = await asyncio.wait_for(self._check(), timeout=self.timeout)
            result.duration = time.time() - start_time
            result.timestamp = datetime.utcnow()
            
            self.logger.debug(
                f"Health check completed",
                status=result.status.value,
                duration=result.duration
            )
            
            return result
            
        except asyncio.TimeoutError:
            duration = time.time() - start_time
            self.logger.warning(f"Health check timeout after {duration:.2f}s")
            
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message="Health check timeout",
                duration=duration,
                timestamp=datetime.utcnow(),
                error="Timeout"
            )
            
        except Exception as e:
            duration = time.time() - start_time
            self.logger.error(f"Health check failed: {e}")
            
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"Health check failed: {str(e)}",
                duration=duration,
                timestamp=datetime.utcnow(),
                error=str(e)
            )

class DatabaseHealthChecker(BaseHealthChecker):
    """数据库健康检查器"""
    
    def __init__(
        self,
        connection_string: str,
        name: str = "database",
        timeout: float = 5.0,
        test_query: str = "SELECT 1"
    ):
        super().__init__(name, timeout)
        self.connection_string = connection_string
        self.test_query = test_query
    
    async def _check(self) -> HealthCheck:
        """检查数据库连接"""
        try:
            # 尝试连接数据库
            conn = await asyncpg.connect(self.connection_string)
            
            try:
                # 执行测试查询
                result = await conn.fetchval(self.test_query)
                
                return HealthCheck(
                    name=self.name,
                    status=HealthStatus.HEALTHY,
                    message="Database connection successful",
                    details={
                        "query_result": result,
                        "connection_string": self.connection_string.split('@')[-1]  # 隐藏敏感信息
                    }
                )
                
            finally:
                await conn.close()
                
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"Database connection failed: {str(e)}",
                error=str(e)
            )

class RedisHealthChecker(BaseHealthChecker):
    """Redis健康检查器"""
    
    def __init__(
        self,
        redis_url: str,
        name: str = "redis",
        timeout: float = 5.0
    ):
        super().__init__(name, timeout)
        self.redis_url = redis_url
    
    async def _check(self) -> HealthCheck:
        """检查Redis连接"""
        try:
            # 连接Redis
            redis = aioredis.from_url(self.redis_url)
            
            try:
                # 执行PING命令
                pong = await redis.ping()
                
                # 获取Redis信息
                info = await redis.info()
                
                return HealthCheck(
                    name=self.name,
                    status=HealthStatus.HEALTHY,
                    message="Redis connection successful",
                    details={
                        "ping_result": pong,
                        "version": info.get("redis_version"),
                        "connected_clients": info.get("connected_clients"),
                        "used_memory": info.get("used_memory_human")
                    }
                )
                
            finally:
                await redis.close()
                
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"Redis connection failed: {str(e)}",
                error=str(e)
            )

class HTTPHealthChecker(BaseHealthChecker):
    """HTTP服务健康检查器"""
    
    def __init__(
        self,
        url: str,
        name: str = None,
        timeout: float = 5.0,
        expected_status: int = 200,
        headers: Dict[str, str] = None
    ):
        service_name = name or url.split("://")[1].split("/")[0]
        super().__init__(f"http_{service_name}", timeout)
        self.url = url
        self.expected_status = expected_status
        self.headers = headers or {}
    
    async def _check(self) -> HealthCheck:
        """检查HTTP服务"""
        try:
            async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=self.timeout)) as session:
                async with session.get(self.url, headers=self.headers) as response:
                    
                    status = HealthStatus.HEALTHY if response.status == self.expected_status else HealthStatus.UNHEALTHY
                    
                    return HealthCheck(
                        name=self.name,
                        status=status,
                        message=f"HTTP service responded with status {response.status}",
                        details={
                            "url": self.url,
                            "status_code": response.status,
                            "expected_status": self.expected_status,
                            "response_headers": dict(response.headers)
                        }
                    )
                    
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"HTTP service check failed: {str(e)}",
                error=str(e)
            )

class DiskSpaceHealthChecker(BaseHealthChecker):
    """磁盘空间健康检查器"""
    
    def __init__(
        self,
        path: str = "/",
        name: str = "disk_space",
        timeout: float = 5.0,
        warning_threshold: float = 0.8,  # 80%
        critical_threshold: float = 0.9   # 90%
    ):
        super().__init__(name, timeout)
        self.path = path
        self.warning_threshold = warning_threshold
        self.critical_threshold = critical_threshold
    
    async def _check(self) -> HealthCheck:
        """检查磁盘空间"""
        try:
            # 获取磁盘使用情况
            disk_usage = psutil.disk_usage(self.path)
            usage_percent = disk_usage.used / disk_usage.total
            
            # 确定状态
            if usage_percent >= self.critical_threshold:
                status = HealthStatus.UNHEALTHY
                message = f"Disk space critically low: {usage_percent:.1%} used"
            elif usage_percent >= self.warning_threshold:
                status = HealthStatus.DEGRADED
                message = f"Disk space warning: {usage_percent:.1%} used"
            else:
                status = HealthStatus.HEALTHY
                message = f"Disk space OK: {usage_percent:.1%} used"
            
            return HealthCheck(
                name=self.name,
                status=status,
                message=message,
                details={
                    "path": self.path,
                    "total": disk_usage.total,
                    "used": disk_usage.used,
                    "free": disk_usage.free,
                    "usage_percent": usage_percent,
                    "warning_threshold": self.warning_threshold,
                    "critical_threshold": self.critical_threshold
                }
            )
            
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"Disk space check failed: {str(e)}",
                error=str(e)
            )

class MemoryHealthChecker(BaseHealthChecker):
    """内存健康检查器"""
    
    def __init__(
        self,
        name: str = "memory",
        timeout: float = 5.0,
        warning_threshold: float = 0.8,  # 80%
        critical_threshold: float = 0.9   # 90%
    ):
        super().__init__(name, timeout)
        self.warning_threshold = warning_threshold
        self.critical_threshold = critical_threshold
    
    async def _check(self) -> HealthCheck:
        """检查内存使用情况"""
        try:
            # 获取内存使用情况
            memory = psutil.virtual_memory()
            usage_percent = memory.percent / 100
            
            # 确定状态
            if usage_percent >= self.critical_threshold:
                status = HealthStatus.UNHEALTHY
                message = f"Memory usage critically high: {usage_percent:.1%} used"
            elif usage_percent >= self.warning_threshold:
                status = HealthStatus.DEGRADED
                message = f"Memory usage warning: {usage_percent:.1%} used"
            else:
                status = HealthStatus.HEALTHY
                message = f"Memory usage OK: {usage_percent:.1%} used"
            
            return HealthCheck(
                name=self.name,
                status=status,
                message=message,
                details={
                    "total": memory.total,
                    "available": memory.available,
                    "used": memory.used,
                    "free": memory.free,
                    "usage_percent": usage_percent,
                    "warning_threshold": self.warning_threshold,
                    "critical_threshold": self.critical_threshold
                }
            )
            
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"Memory check failed: {str(e)}",
                error=str(e)
            )

class CPUHealthChecker(BaseHealthChecker):
    """CPU健康检查器"""
    
    def __init__(
        self,
        name: str = "cpu",
        timeout: float = 5.0,
        warning_threshold: float = 0.8,  # 80%
        critical_threshold: float = 0.9,  # 90%
        interval: float = 1.0  # CPU使用率采样间隔
    ):
        super().__init__(name, timeout)
        self.warning_threshold = warning_threshold
        self.critical_threshold = critical_threshold
        self.interval = interval
    
    async def _check(self) -> HealthCheck:
        """检查CPU使用情况"""
        try:
            # 获取CPU使用率（需要间隔采样）
            cpu_percent = psutil.cpu_percent(interval=self.interval)
            usage_percent = cpu_percent / 100
            
            # 获取CPU核心数
            cpu_count = psutil.cpu_count()
            
            # 确定状态
            if usage_percent >= self.critical_threshold:
                status = HealthStatus.UNHEALTHY
                message = f"CPU usage critically high: {usage_percent:.1%} used"
            elif usage_percent >= self.warning_threshold:
                status = HealthStatus.DEGRADED
                message = f"CPU usage warning: {usage_percent:.1%} used"
            else:
                status = HealthStatus.HEALTHY
                message = f"CPU usage OK: {usage_percent:.1%} used"
            
            return HealthCheck(
                name=self.name,
                status=status,
                message=message,
                details={
                    "usage_percent": usage_percent,
                    "cpu_count": cpu_count,
                    "warning_threshold": self.warning_threshold,
                    "critical_threshold": self.critical_threshold
                }
            )
            
        except Exception as e:
            return HealthCheck(
                name=self.name,
                status=HealthStatus.UNHEALTHY,
                message=f"CPU check failed: {str(e)}",
                error=str(e)
            )

class HealthCheckManager:
    """健康检查管理器"""
    
    def __init__(self):
        self.checkers: Dict[str, IHealthChecker] = {}
        self.logger = get_logger("health_manager")
        self.event_bus = get_event_bus()
    
    def register(self, checker: IHealthChecker):
        """注册健康检查器"""
        self.checkers[checker.name] = checker
        self.logger.info(f"Registered health checker: {checker.name}")
    
    def unregister(self, name: str):
        """注销健康检查器"""
        if name in self.checkers:
            del self.checkers[name]
            self.logger.info(f"Unregistered health checker: {name}")
    
    async def check(self, name: str) -> Optional[HealthCheck]:
        """执行单个健康检查"""
        checker = self.checkers.get(name)
        if not checker:
            return None
        
        return await checker.check()
    
    async def check_all(self) -> Dict[str, HealthCheck]:
        """执行所有健康检查"""
        results = {}
        
        # 并发执行所有检查
        tasks = []
        for name, checker in self.checkers.items():
            task = asyncio.create_task(checker.check())
            tasks.append((name, task))
        
        # 等待所有检查完成
        for name, task in tasks:
            try:
                result = await task
                results[name] = result
                
                # 发布健康检查事件
                self.event_bus.publish(Event(
                    event_type=f"health.{result.status.value}",
                    data={
                        "checker": name,
                        "status": result.status.value,
                        "message": result.message,
                        "duration": result.duration
                    }
                ))
                
            except Exception as e:
                self.logger.error(f"Health check {name} failed: {e}")
                results[name] = HealthCheck(
                    name=name,
                    status=HealthStatus.UNHEALTHY,
                    message=f"Health check failed: {str(e)}",
                    error=str(e),
                    timestamp=datetime.utcnow()
                )
        
        return results
    
    async def get_overall_status(self) -> HealthStatus:
        """获取整体健康状态"""
        results = await self.check_all()
        
        if not results:
            return HealthStatus.UNKNOWN
        
        statuses = [result.status for result in results.values()]
        
        # 如果有任何检查不健康，整体状态为不健康
        if HealthStatus.UNHEALTHY in statuses:
            return HealthStatus.UNHEALTHY
        
        # 如果有任何检查降级，整体状态为降级
        if HealthStatus.DEGRADED in statuses:
            return HealthStatus.DEGRADED
        
        # 如果所有检查都健康，整体状态为健康
        if all(status == HealthStatus.HEALTHY for status in statuses):
            return HealthStatus.HEALTHY
        
        # 其他情况返回未知
        return HealthStatus.UNKNOWN
    
    async def get_health_summary(self) -> Dict[str, Any]:
        """获取健康状态摘要"""
        results = await self.check_all()
        overall_status = await self.get_overall_status()
        
        return {
            "status": overall_status.value,
            "timestamp": datetime.utcnow().isoformat(),
            "checks": {
                name: {
                    "status": result.status.value,
                    "message": result.message,
                    "duration": result.duration,
                    "timestamp": result.timestamp.isoformat() if result.timestamp else None
                }
                for name, result in results.items()
            },
            "summary": {
                "total": len(results),
                "healthy": sum(1 for r in results.values() if r.status == HealthStatus.HEALTHY),
                "unhealthy": sum(1 for r in results.values() if r.status == HealthStatus.UNHEALTHY),
                "degraded": sum(1 for r in results.values() if r.status == HealthStatus.DEGRADED),
                "unknown": sum(1 for r in results.values() if r.status == HealthStatus.UNKNOWN)
            }
        }

# 全局健康检查管理器
_health_manager: Optional[HealthCheckManager] = None

def get_health_manager() -> HealthCheckManager:
    """获取全局健康检查管理器"""
    global _health_manager
    if _health_manager is None:
        _health_manager = HealthCheckManager()
    return _health_manager

def set_health_manager(manager: HealthCheckManager):
    """设置全局健康检查管理器"""
    global _health_manager
    _health_manager = manager

# 装饰器
def health_check(name: str = None, timeout: float = 5.0):
    """健康检查装饰器"""
    def decorator(func: Callable[[], Awaitable[bool]]):
        check_name = name or f"{func.__module__}.{func.__name__}"
        
        class FunctionHealthChecker(BaseHealthChecker):
            def __init__(self):
                super().__init__(check_name, timeout)
                self.func = func
            
            async def _check(self) -> HealthCheck:
                try:
                    is_healthy = await self.func()
                    status = HealthStatus.HEALTHY if is_healthy else HealthStatus.UNHEALTHY
                    message = f"Function check {'passed' if is_healthy else 'failed'}"
                    
                    return HealthCheck(
                        name=self.name,
                        status=status,
                        message=message
                    )
                except Exception as e:
                    return HealthCheck(
                        name=self.name,
                        status=HealthStatus.UNHEALTHY,
                        message=f"Function check failed: {str(e)}",
                        error=str(e)
                    )
        
        # 注册健康检查器
        checker = FunctionHealthChecker()
        get_health_manager().register(checker)
        
        return func
    
    return decorator

# 示例使用
if __name__ == "__main__":
    import asyncio
    
    async def main():
        # 创建健康检查管理器
        manager = get_health_manager()
        
        # 注册各种健康检查器
        manager.register(DatabaseHealthChecker(
            "postgresql://user:pass@localhost/db",
            name="main_database"
        ))
        
        manager.register(RedisHealthChecker(
            "redis://localhost:6379",
            name="cache_redis"
        ))
        
        manager.register(HTTPHealthChecker(
            "http://api.example.com/health",
            name="external_api"
        ))
        
        manager.register(DiskSpaceHealthChecker("/"))
        manager.register(MemoryHealthChecker())
        manager.register(CPUHealthChecker())
        
        # 执行健康检查
        health_summary = await manager.get_health_summary()
        print(f"Overall status: {health_summary['status']}")
        
        for name, check in health_summary['checks'].items():
            print(f"  {name}: {check['status']} - {check['message']}")
    
    # 使用健康检查装饰器
    @health_check("custom_service")
    async def check_custom_service() -> bool:
        # 自定义健康检查逻辑
        return True
    
    asyncio.run(main())
